package com.platform.boot.security;

import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.security.web.reactive.result.view.CsrfRequestDataValueProcessor;
import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebSession;
import org.springframework.web.server.session.WebSessionManager;
import reactor.core.publisher.Mono;

/**
 * @author <a href="https://github.com/vnobo">Alex bob</a>
 */
@Tag(name = "登录管理")
@RestController
@RequestMapping("/oauth2/v1/")
@RequiredArgsConstructor
public class SecurityController {
    private final SecurityUserManager userDetailsService;
    private final WebSessionManager webSessionManager;

    @GetMapping("csrf")
    public Mono<CsrfToken> csrfToken(ServerWebExchange exchange) {
        CsrfToken csrfToken = exchange.getRequiredAttribute(CsrfRequestDataValueProcessor.DEFAULT_CSRF_ATTR_NAME);
        return Mono.defer(() -> Mono.just(csrfToken));
    }

    @GetMapping("token")
    public Mono<AuthenticationToken> token(WebSession session) {
        return Mono.just(AuthenticationToken.build(session));
    }

    @GetMapping("refresh")
    public Mono<AuthenticationToken> refresh(ServerWebExchange exchange, Authentication authentication) {
        ServerWebExchange exchangeNew = exchange.mutate().request(exchange.getRequest().mutate()
                .headers(httpHeaders -> {
                    httpHeaders.remove("X-Auth-Token");
                    httpHeaders.remove("Cookie");
                }).build()).build();
        Mono<WebSession> webSessionMono = webSessionManager.getSession(exchangeNew);
        return this.userDetailsService.findByUsername(authentication.getName())
                .map(userDetails -> new SecurityContextImpl(new UsernamePasswordAuthenticationToken(
                        userDetails, userDetails.getPassword(), userDetails.getAuthorities())))
                .flatMap(securityContext -> webSessionMono.doOnNext(webSession -> webSession.getAttributes()
                        .put(WebSessionServerSecurityContextRepository.DEFAULT_SPRING_SECURITY_CONTEXT_ATTR_NAME,
                                securityContext)))
                .delayUntil(WebSession::save)
                .map(AuthenticationToken::build)
                .contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication));
    }


    @GetMapping("me")
    public Mono<SecurityUserDetails> me() {
        return ReactiveSecurityUserHolder.getContext()
                .delayUntil(securityDetails -> this.userDetailsService.loginSuccess(securityDetails.getUsername()));
    }
}